#include <Bitmap.hpp>
#include <Ray.hpp>

static float hit_sphere(const glm::vec3 &center, float radius, const Ray &ray) {
  auto oc = ray.Origin() - center;
  auto a = glm::dot(ray.Direction(), ray.Direction());
  auto half_b = glm::dot(oc, ray.Direction());
  auto c = glm::dot(oc, oc) - radius * radius;
  auto discriminant = half_b * half_b - a * c;

  if (discriminant < 0.f) {
    return -1.f;
  } else {
    return (-half_b - glm::sqrt(discriminant)) / a;
  }
}

static glm::vec3 ray_color(const Ray &ray) {
  auto t = hit_sphere(glm::vec3{0.f, 0.f, -1.f}, 0.5f, ray);
  if (t > 0.f) {
    glm::vec3 N = glm::normalize(ray.At(t) - glm::vec3{0.f, 0.f, -1.f});
    return 0.5f * (N + 1.f);
  }

  return Ray::RayColor(ray);
}

int main(int argc, const char **argv) {
  auto bitmap = Bitmap::CreateBitmap(400, 16.f / 9.f);

  float viewport_height = 2.f;
  float viewport_width = bitmap->PixelRadio() * viewport_height;
  auto focal_length = 1.f;

  glm::vec3 origin(0.f, 0.f, 0.f);
  auto horizontal = glm::vec3(viewport_width, 0.f, 0.f);
  auto vertical = glm::vec3(0.f, viewport_height, 0.f);

  auto lower_left_corner = origin - horizontal / 2.f - vertical / 2.f -
                           glm::vec3(0, 0, focal_length);

  for (uint32_t i = 0; i < bitmap->Width(); i++) {
    for (uint32_t j = 0; j < bitmap->Height(); j++) {
      auto u = float(i) / (bitmap->Width() - 1);
      auto v = float(j) / (bitmap->Height() - 1);

      Ray ray(origin,
              lower_left_corner + u * horizontal + v * vertical - origin);

      auto color = ray_color(ray);

      bitmap->setPixel(i, bitmap->Height() - 1 - j, color);
    }
  }

  bitmap->writeToFile("sphere_surface_normal.png");
  return 0;
}